#include "config.h"

#include <sys/types.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/statvfs.h>

#define DBG_SUBSYS S_LIBCLUSTER

#include "sysy_lib.h"
#include "env.h"
#include "configure.h"
#include "fence.h"
#include "lichconf.h"
#include "job_dock.h"
#include "minirpc.h"
#include "rpc_proto.h"
#include "net_global.h"
#include "etcd.h"
#include "../nodepool/nodeid.h"
#include "metadata.h"
#include "cluster.h"
#include "../dispatch/dispatch.h"
#include "net_table.h"
#include "conn.h"
#include "ynet_rpc.h"
#include "node.h"
#include "net_vip.h"
#include "ylog.h"
#include "disk.h"
#include "bh_task.h"
#include "rmsnap_bh.h"
#include "stor_root.h"
#include "system.h"
#include "../../storage/storage/local_vol.h"
#include "../../storage/controller/castoff.h"
#include "lease_ctl.h"
#include "recovery.h"
#include "lease.h"
#include "dbg.h"
#include "storage_status.h"

#define NODE_HB_INTERVAL 10

extern node_t __node__;

admin_t *__admin__;
int __inited__ = 0;
int __reloading__ = 0;

static inline int __is_admin()
{

        if (__admin__ == NULL || __inited__ == 0)
                return 0;
        else
                return 1;
}

typedef struct {
        uint32_t crc;
        uint64_t id;
} saveid_t;

#define __FILEID__ "id"
#define __FILEID_STEP__ 1024

static int __node_admin_createid()
{
        int ret;
        saveid_t saveid;

        saveid.id = __FILEID_STEP__;
        saveid.crc = crc32_sum(&saveid.id, sizeof(saveid.id));
        ret = etcd_create(ETCD_MISC, __FILEID__, &saveid, sizeof(saveid), 0);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        __admin__->seq = ROOTID + 1;

        return 0;
err_ret:
        return ret;
}

static int __node_admin_update(uint64_t newid)
{
        int ret;
        saveid_t saveid;

        saveid.id = newid;
        saveid.crc = crc32_sum(&saveid.id, sizeof(saveid.id));
        ret = etcd_update(ETCD_MISC, __FILEID__, &saveid, sizeof(saveid),
                          NULL, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        YASSERT(newid % __FILEID_STEP__ == 0);
        DINFO("update id %ju\n", saveid.id);

        return 0;
err_ret:
        return ret;
}

static int __node_admin_loadid()
{
        int ret, buflen;
        saveid_t saveid;

        buflen = sizeof(saveid);
        ret = etcd_get_bin(ETCD_MISC, __FILEID__, &saveid, &buflen, NULL);
        if (unlikely(ret)) {
                if (ret == ENOKEY) {
                        ret = __node_admin_createid();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else
                        GOTO(err_ret, ret);
        } else {
                YASSERT(buflen == sizeof(saveid));
                ret = __node_admin_update(saveid.id + __FILEID_STEP__);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                DINFO("update id %ju\n", saveid.id);
                __admin__->seq = saveid.id + 1;

                YASSERT(saveid.id % __FILEID_STEP__ == 0);
        }

        return 0;
err_ret:
        return ret;
}

static int __node_admin_newid(uint64_t *id)
{
        int ret;

        if (__admin__->seq == (uint64_t)-1) {
                ret = __node_admin_loadid();
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        if (__admin__->seq % __FILEID_STEP__ == 0) {
                ret = __node_admin_update(__admin__->seq + __FILEID_STEP__);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        __admin__->seq++;
        *id = __admin__->seq;

        YASSERT(*id > 1);
        DINFO("new id %ju\n", *id);

        return 0;
err_ret:
        return ret;
}

static int __node_admin_srv_start()
{
        int ret;
        admin_t *admin;

        YASSERT(__admin__ == NULL);
        ret = ymalloc((void**)&admin, sizeof(*admin));
        if (unlikely(ret))
                GOTO(err_ret, ret);


        memset(admin, 0x0, sizeof(*admin));

        ret = sy_rwlock_init(&admin->lock, "node_srv.lock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        admin->seq = -1;
        admin->uptime = gettime();

        __admin__ = admin;
        __inited__ = 1;

        return 0;
err_ret:
        return ret;
}

static void  __node_admin_srv_stop()
{
        int ret;
        admin_t *admin = __admin__;

        __inited__ = 0;
        __admin__ = NULL;
        
        ret = sy_rwlock_wrlock(&admin->lock);
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        __inited__ = 0;

        sy_rwlock_unlock(&admin->lock);

        yfree((void **)&admin);
}


static int __node_admin_start()
{
        int ret;

        ret = __node_admin_srv_start();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = nodetable_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = dispatch_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = lease_rpc_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = lease_ctl_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        ret = bh_task_start();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = system_set_createtime();
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        DINFO("start admin\n");

        return 0;
err_ret:
        return ret;
}


static void __node_admin_stop()
{
        if (!__is_admin()) {
                DWARN("node status is %s\n", __node__.status);
                return;
        }

        DINFO("admin stop\n");

        __reloading__ = 0;
        netvip_destroy();
        bh_task_stop();
        lease_rpc_destroy();
        dispatch_destroy();
        __node_admin_srv_stop();
        nodetable_destroy();
        lease_ctl_destroy();
}

static void __node_admin_reload()
{
        DINFO("admin reload\n");
        
        __reloading__ = 1;
        netvip_destroy();
        bh_task_stop();
        lease_rpc_destroy();
        dispatch_destroy();
        __node_admin_srv_stop();
        nodetable_destroy();
        lease_ctl_destroy();
}

static int  __node_admin_create()
{
        int ret;
        char tmp[MAX_BUF_LEN], path[MAX_PATH_LEN];
        nid_t nid;
        nodestat_t stat;

        snprintf(path, MAX_NAME_LEN, "%s/config/nid", __node__.home);
        ret = _get_text(path, tmp, MAX_NAME_LEN);
        if (ret < 0) {
                ret = -ret;
                if (ret == ENOENT) {
                        DINFO("empty nid\n");
                } else
                        GOTO(err_ret, ret);
        } else {
                goto out;
        }

        ret = nodetable_get(__node__.name, &stat);
        if (ret) {
                if (ret == ENOENT) {
#if 0
                        ret = nodeid_newid(&nid.id, __node__.name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
#endif

                        ret = nodetable_new(__node__.name, &nid);
                        if (unlikely(ret)) {
                                GOTO(err_ret, ret);
                        }
                } else {
                        UNIMPLEMENTED(__DUMP__);
                }
        } else {
                nid = stat.nid;
        }

        net_setnid(&nid);

        nid2str(tmp, &nid);
        ret = _set_text(path, tmp, strlen(tmp) + 1, O_CREAT);
        if (unlikely(ret)) {
                UNIMPLEMENTED(__DUMP__);
                GOTO(err_ret, ret);
        }

        DINFO("set local nid "NID_FORMAT"\n", NID_ARG(net_getnid()));

#if 0
        ret = etcd_create_text(ETCD_NODE, __node__.name, tmp, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);
#endif

out:
        return 0;
err_ret:
        return ret;
}

/**
 * @brief 负责节点与admin的心跳，通信内容包括：
 * - 存储池信息
 * - 本地卷控制器信息
 *
 * @param lock
 * @param master
 * @return
 */

static int __node_admin_trylock(etcd_lock_t *lock)
{
        int ret, retry = 0;

        DINFO("master reload\n");
        __node_admin_reload();

        while (1) {
                ret = etcd_lock(lock);
                if (unlikely(ret)) {
                        if (ret == EEXIST)
                                GOTO(err_ret, ret);
                        else {
                                DWARN("master lock fail, retry %u\n", retry);
                                retry++;
                                continue;
                        }
                }

                DINFO("master reload success\n");
                break;
        }
                        
        return 0;
err_ret:
        return ret;
}

int node_admin_start(etcd_lock_t *lock, const char *master)
{
        int ret, step = 0;
        time_t t = gettime();

        DINFO("start as new admin\n");
        SINFO(0, "%s, start as admin\n", M_FUSIONSTOR_START);

        DINFO("set master magic 0x%x\n", lock->magic);
        ng.master_magic = lock->magic;
        
retry:
        //DINFO("========  run as master ========\n");

        ret = netvip_setvip();
        if (unlikely(ret)) {
                DWARN("master set vip failed ret( %d )\n", ret);
        }
        
        ret = __node_admin_start();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = __node_admin_create();
        if (unlikely(ret))
                GOTO(err_stop, ret);

        //DINFO("set admin %u %s\n", net_getnid()->id, master);
        net_setadmin(net_getnid());

#if !ENABLE_START_PARALLEL
        ret = conn_register();
        if (unlikely(ret))
                GOTO(err_stop, ret);
#endif

        strcpy(__node__.status, NODE_STATUS_ADMIN);
        strcpy(__node__.admin, master);

        node_srv_heartbeat(1);

        while (1) {
                if (step % NODE_HB_INTERVAL == 0) {
                        //net_setadmin(net_getnid());
                        node_srv_heartbeat(0);
                }

                if (step % STORAGE_STATUS_UPTIME == 0) {
                        storage_status_update();
                }

                if (!etcd_lock_health(lock)) {
                        ret = __node_admin_trylock(lock);
                        if (unlikely(ret)) {
                                DERROR("master fail live time %u\n", (int)(gettime() - t));
                                ret = EAGAIN;
                                GOTO(err_stop, ret);
                        } else {
                                DINFO("master retry success\n");
                                goto retry;
                        }
                }

                step++;
                sleep(1);
        }

        return 0;
err_stop:
        __node_admin_stop();
err_ret:
        return ret;
}

int node_srv_admin_newid(chkid_t *chkid)
{
        int ret;

        memset(chkid, 0x0, sizeof(*chkid));
        ret = sy_rwlock_wrlock(&__admin__->lock);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (!__is_admin()) {
                ret = ENOSYS;
                GOTO(err_lock, ret);
        }
        
        ret = __node_admin_newid(&chkid->id);
        if (unlikely(ret))
                GOTO(err_lock, ret);

        sy_rwlock_unlock(&__admin__->lock);

        return 0;
err_lock:
        sy_rwlock_unlock(&__admin__->lock);
err_ret:
        return ret;
}

int node_srv_admin_version(uint32_t *version)
{
        int ret;
        
        ret = sy_rwlock_rdlock(&__admin__->lock);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (!__is_admin()) {
                ret = ENOSYS;
                GOTO(err_lock, ret);
        }
        
        *version = __admin__->uptime;

        sy_rwlock_unlock(&__admin__->lock);

        return 0;
err_lock:
        sy_rwlock_unlock(&__admin__->lock);
err_ret:
        return ret;
}

int node_srv_admin_join(const char *name)
{
        int ret;
        nid_t nid;
        nodestat_t stat;

        if (!__is_admin()) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        ret = nodetable_get(name, &stat);
        if (ret == 0) {
                ret = EEXIST;
                DWARN("node %s exist\n", name);
                GOTO(err_ret, ret);
        }

        ret = nodetable_new(name, &nid);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        DINFO("add node %s, nid %u\n", name, nid.id);
        
        return 0;
err_ret:
        return ret;
}


int node_srv_admin_check()
{
        int ret;
        ret = __is_admin() || __reloading__;
        DINFO("node is %s. %u,%u,%u\n", ret ? "admin" : "normal", __admin__, __inited__, __reloading__);

        return ret;
}
